跳到主要内容

主进程如何捕获未处理异常?如何做崩溃守护与自动拉起?

考察点​

  • Node 异常兜底机制
  • agent 类应用的高可用设计(崩溃日志 + 自动恢复)

参考答案​

1. 异常捕获​

// 同步未捕获异常
process.on('uncaughtException', (err) => {
logger.error('主进程未捕获异常:', err);
// 只记录日志 + 优雅退出,不要吞掉异常继续跑(状态已不可信)
gracefulExit(1);
});

// Promise 未处理的 rejection
process.on('unhandledRejection', (reason) => {
logger.error('未处理的 Promise rejection:', reason);
});

关键认知:uncaughtException 触发后进程状态已不可靠,正确姿势是记录日志后优雅退出,靠守护机制拉起,而不是 catch 住当没事发生。

2. 崩溃日志采集​

const { crashReporter } = require('electron');

crashReporter.start({
productName: 'MyAgent',
companyName: 'MyCompany',
submitURL: 'https://crash.example.com/minidump',
uploadToServer: true, // minidump 崩溃转储文件自动上报
});
  • crashReporter 采集的是 Chromium 层崩溃(minidump),与 JS 异常互补
  • 配合 breakpad 符号表做堆栈符号化(或用 Sentry 的 Electron SDK 一站搞定)

3. 崩溃守护 / 自动拉起​

方案一:父子进程守护(自研常用)

// watchdog 进程:启动时 fork 真正的工作进程
const { fork } = require('child_process');

function startWorker() {
const worker = fork(path.join(__dirname, 'worker.js'));
worker.on('exit', (code) => {
if (code !== 0) {
logger.warn(`工作进程异常退出 code=${code},3 秒后重启`);
setTimeout(startWorker, 3000); // 延迟重启,避免崩溃风暴
}
});
}

方案二:借助系统能力

  • Windows:注册为服务(node-windows),由 SCM 负责拉起
  • macOS:launchd 的 KeepAlive 配置
  • Linux:systemd Restart=always

方案三:Electron 内重载渲染进程 渲染进程崩溃不重启应用,监听事件重载即可:

win.webContents.on('render-process-gone', (e, details) => {
logger.error('渲染进程崩溃:', details.reason);
win.reload(); // 直接重载恢复
});

4. 防崩溃风暴​

无论哪种方案都要加:

  • 重启频率限制(如 1 分钟内崩溃 3 次则停止拉起,提示用户)
  • 崩溃标记位:上次异常退出则在下次启动时进入"安全模式"(禁用可疑模块)

面试话术​

我做的 agent 采用「主进程兜底日志 + 优雅退出 + 外部 watchdog 拉起」三层机制:JS 层双事件捕获写日志;崩溃走 minidump 上报并做符号化;拉起限制频率防止崩溃风暴,且带安全模式兜底。

常见追问​

  • 主进程崩溃时渲染进程会怎样? 整个应用退出(主进程是根),所以渲染侧的心跳/重连只能防渲染进程自己的问题。
  • 为什么不用 try/catch 包全局? Node 的异步错误捕获不到,必须靠进程级事件。